package com.andaily.domain.developer;

import com.andaily.domain.AbstractDomain;
import com.andaily.domain.developer.operation.ScrumSecurityChecker;
import com.andaily.domain.developer.operation.SprintTimeUtils;
import com.andaily.domain.shared.DateUtils;
import com.andaily.domain.user.User;
import com.andaily.infrastructure.support.LogHelper;
import com.andaily.web.context.BeanProvider;

import java.util.Date;

/**
 * Date: 13-8-3
 *
 * @author Shengzhao Li
 */
public class SprintTask extends AbstractDomain {

    private static LogHelper log = LogHelper.create(SprintTask.class);
    private transient SprintRepository sprintRepository = BeanProvider.getBean(SprintRepository.class);

    private String name;
    private User creator;
    /**
     * The task estimate  time (unit: minute)
     */
    private int estimateTime;

    /**
     * The task actual used time (unit: minute)
     */
    private int actualUsedTime;

    private Sprint sprint;
    //Default status is Created
    private SprintTaskStatus status = SprintTaskStatus.CREATED;

    private Date pendingTime;
    private User executor;

    private Date finishTime;

    private Date cancelTime;
    private User cancelUser;

    private SprintPriority priority = SprintPriority.DEFAULT;
    //If this is an urgent task, the value is true
    private boolean urgent;
    /**
     * null is available, If it is not null ,
     * the backlog should be belong to {@link Sprint#backlogs} .
     */
    private Backlog backlog;

    private String description;

    public SprintTask() {
    }

    public SprintTask(String name, int estimateTime, SprintPriority priority, boolean urgent) {
        this.name = name;
        this.estimateTime = estimateTime;
        this.priority = priority;
        this.urgent = urgent;
    }

    public SprintTask updateSprint(Sprint sprint) {
        this.sprint = sprint;
        return this;
    }

    public String number() {
        String tempText = "00000" + id();
        int length = tempText.length();
        return tempText.substring(length - 5, length);
    }

    public String name() {
        return name;
    }

    public User creator() {
        return creator;
    }

    public int estimateTime() {
        return estimateTime;
    }

    public int actualUsedTime() {
        return actualUsedTime;
    }

    public Sprint sprint() {
        return sprint;
    }

    public SprintTaskStatus status() {
        return status;
    }

    public Date pendingTime() {
        return pendingTime;
    }

    public User executor() {
        return executor;
    }

    public Date finishTime() {
        return finishTime;
    }

    public Date cancelTime() {
        return cancelTime;
    }

    public User cancelUser() {
        return cancelUser;
    }

    public SprintPriority priority() {
        return priority;
    }

    public boolean urgent() {
        return urgent;
    }

    public Backlog backlog() {
        return backlog;
    }

    public SprintTask updateCreator(User creator) {
        this.creator = creator;
        return this;
    }

    public SprintTask updateName(String name) {
        this.name = name;
        return this;
    }

    public String description() {
        return description;
    }

    public String actualUsedTimeAsHour() {
        return SprintTimeUtils.taskTimeAsString(actualUsedTime);
    }

    /**
     * Return <i>estimateTime - actualUsedTime</i>.
     *
     * @return time difference
     */
    public int timeDifference() {
        return (estimateTime - actualUsedTime);
    }

    public String estimateTimeAsHour() {
        return SprintTimeUtils.taskTimeAsString(estimateTime);
    }

    public SprintTask updateBacklog(Backlog backlog) {
        this.backlog = backlog;
        return this;
    }

    public SprintTask updateDescription(String description) {
        this.description = description;
        return this;
    }

    public SprintTask updatePriority(SprintPriority priority) {
        this.priority = priority;
        return this;
    }

    public SprintTask updateUrgent(boolean urgent) {
        this.urgent = urgent;
        return this;
    }

    public SprintTask updateEstimateTime(int estTime) {
        this.estimateTime = estTime;
        return this;
    }

    /**
     * When update status, update the reference time too;
     * E.g.   update status to PENDING, then set pendingTime is now().
     *
     * @param status SprintTaskStatus
     * @return SprintTask
     */
    public SprintTask updateStatus(SprintTaskStatus status) {
        this.status = status;
        if (SprintTaskStatus.PENDING.equals(status)) {
            this.pendingTime = DateUtils.now();
        } else if (SprintTaskStatus.CANCELED.equals(status)) {
            this.cancelTime = DateUtils.now();
        } else if (SprintTaskStatus.FINISHED.equals(status)) {
            this.finishTime = DateUtils.now();
        }
        log.info("Change the SprintTask " + this + " status to {" + status + "}");
        return this;
    }

    public void saveOrUpdate() {
        if (isNewly()) {
            sprintRepository.saveSprintTask(this);
        } else {
            sprintRepository.updateSprintTask(this);
        }
    }

    @Override
    public String toString() {
        return "{" +
                "name='" + name + '\'' +
                ", guid=" + guid +
                ", id=" + id +
                ", estimateTime=" + estimateTime +
                '}';
    }

    public SprintTask revert() {
        ScrumSecurityChecker.checkSprintTask(this, SprintTaskStatus.PENDING);
        this.status = SprintTaskStatus.CREATED;
        this.pendingTime = null;
        this.executor = null;
        log.info("Revert the SprintTask " + this + " to {" + status + "}");
        return this;
    }

    public SprintTask finish(String usedTime) {
        ScrumSecurityChecker.checkSprintTask(this, SprintTaskStatus.PENDING);
        this.status = SprintTaskStatus.FINISHED;
        this.finishTime = DateUtils.now();
        this.actualUsedTime = SprintTimeUtils.taskTimeAsMinute(usedTime);

        log.info("Finish the SprintTask " + this + ", actual use time {" + usedTime + "}");
        return this;
    }

    public SprintTask restore() {
        ScrumSecurityChecker.checkSprintTask(this, SprintTaskStatus.CANCELED);
        this.status = SprintTaskStatus.CREATED;
        this.cancelTime = null;
        this.cancelUser = null;

        log.info("Restore the SprintTask " + this + " to {" + status + "}");
        return this;
    }

    public SprintTask start() {
        ScrumSecurityChecker.checkSprintTask(this, SprintTaskStatus.CREATED);
        ScrumSecurityChecker.checkSprint(this.sprint(), SprintStatus.PENDING);

        this.status = SprintTaskStatus.PENDING;
        this.pendingTime = DateUtils.now();
//        this.executor = currentUser;

        log.info("Start the SprintTask " + this + ", status is {" + status + "} now");
        return this;
    }

    /**
     * The task is moved from another sprint or not.
     *
     * @return True is moved,otherwise false
     */
    public boolean moved() {
        int count = sprintRepository.countSprintTaskMoveRecords(this);
        return count > 0;
    }
}
